package com.lianqi.emcpframework.ddd.gencode.plugin;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.util.List;

import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;

import org.apache.commons.lang3.StringUtils;

import com.lianqi.emcpframework.ddd.gencode.EnableGenForAggregate;
import com.lianqi.emcpframework.ddd.gencode.EnableGenForEntity;
import com.lianqi.emcpframework.ddd.gencode.define.JavaSource;
import com.lianqi.emcpframework.ddd.gencode.dto.GenDto;
import com.lianqi.emcpframework.ddd.gencode.dto.GenDtoIgnore;
import com.lianqi.emcpframework.ddd.gencode.dto.GenDtoParser;
import com.lianqi.emcpframework.ddd.gencode.dto.support.meta.DtoGetterMeta;
import com.lianqi.emcpframework.ddd.gencode.dto.support.meta.DtoMeta;
import com.lianqi.emcpframework.ddd.gencode.dto.support.parser.DtoMetaParser;
import com.lianqi.emcpframework.ddd.gencode.dto.support.writer.DefaultDtoMethodWriter;
import com.lianqi.emcpframework.ddd.gencode.dto.support.writer.DtoMethodWriter;
import com.lianqi.emcpframework.ddd.gencode.dto.support.writer.FileLocationDtoWriter;
import com.lianqi.emcpframework.ddd.gencode.dto.support.writer.InstantToLongDtoWriter;
import com.squareup.javapoet.*;

import lombok.Data;

public class GenDtoPlugin extends AbstractProcessorPlugin {
    private final DtoMetaParser dtoMetaParser = new DtoMetaParser();
    private final DtoMethodWriter fileLocationVoWriter = new FileLocationDtoWriter();
    private final DtoMethodWriter instantToLongVoWriter = new InstantToLongDtoWriter();
    // private final DtoMethodWriter selfDescribedEnumVoWriter = new SelfDescribedEnumDtoWriter();
    private final DtoMethodWriter defaultMethodWriter = new DefaultDtoMethodWriter();

    @Override
    protected void process(TypeElement element, Annotation annotation) {
        GenDtoParser parser = new GenDtoParser(element);
        parser.read(annotation);

        String className = parser.getClassName();

        String packageName = parser.getPackageName();

        String parentClassName = parser.getParentClassName();

        TypeSpec.Builder typeSpecBuilder =
            TypeSpec.classBuilder(className).addSuperinterface(TypeName.get(Serializable.class))
                .addAnnotation(Data.class).addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT);

        if (StringUtils.isNotEmpty(parentClassName)) {
            ClassName parent = ClassName.bestGuess(parentClassName);
            if (parser.isUseIdType() && StringUtils.isNotEmpty(parser.getIdType())) {
                TypeName typeName = TypeVariableName.get(parser.getIdType());
                ParameterizedTypeName parameterizedTypeName = ParameterizedTypeName.get(parent, typeName);
                typeSpecBuilder.superclass(parameterizedTypeName);
            } else {
                typeSpecBuilder.superclass(parent);
            }
        }

        MethodSpec.Builder cMethodSpecBuilder = MethodSpec.constructorBuilder()
            .addParameter(TypeName.get(element.asType()), "source").addModifiers(Modifier.PUBLIC);

        if (StringUtils.isNotEmpty(parentClassName)) {
            cMethodSpecBuilder.addStatement("super(source)");
        }

        DtoMeta dtoMeta = this.dtoMetaParser.parse(element);
        List<DtoGetterMeta> metas = dtoMeta.getMethodMetas();
        this.fileLocationVoWriter.write(typeSpecBuilder, cMethodSpecBuilder, metas);
        this.instantToLongVoWriter.write(typeSpecBuilder, cMethodSpecBuilder, metas);
        // this.selfDescribedEnumVoWriter.write(typeSpecBuilder, cMethodSpecBuilder, metas);
        this.defaultMethodWriter.write(typeSpecBuilder, cMethodSpecBuilder, metas);

        typeSpecBuilder.addMethod(cMethodSpecBuilder.build());
        getJavaSourceCollector().register(new JavaSource(packageName, className, typeSpecBuilder));

    }

    @Override
    public Class<Annotation>[] applyAnnCls() {
        return new Class[] {GenDto.class, EnableGenForAggregate.class, EnableGenForEntity.class};
    }

    @Override
    public <A extends Annotation> Class<A>[] ignoreAnnCls() {
        return new Class[] {GenDtoIgnore.class};
    }
}
